﻿using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Administration;

namespace TomShirley.WebParts.AccessChecker
{
    /// <summary>
    /// Input area used to capture configuration settings for use within a SPPermissionsTreeView object.
    /// </summary>
    public class AccessCheckerInputForm : Panel
    {

        #region Private Properties

        private SPWeb _ContextWeb;
        private SPSite _ContextSite;
        private Page _Page; // Reference to a page object required when creating SharePoint controls

        /// <summary>
        /// Stores the mode of the Tree, i.e. 'User' Mode or 'Web' Mode. This property needs to be set before the
        /// PreRender step.
        /// </summary>
        private AccessCheckerMode _Mode;
        // Contains user information
        private SPPrincipalInfo _User;
        // Conatins the SPRoleDefinition that is set by the user. The _RoleDefinition
        // is used to determine if the user in _PrincipalInfo has the base permissions in _RoleDefinition
        // on a securable object.
        private SPRoleDefinition _AccessLevel;

        // Authentication method that the current web app is using (AD-Windows, FBA-Forms)
        private SupportedAuthenticationModes _AuthenticationMode;

        // Controls for selecting AD User/Group and role definition within web part.
        private ToolBar _Toolbar;
        private PeopleEditor _PeoplePicker;
        private DropDownList _RoleNameList;

        private Button _GenerateTree;
        private RadioButtonList _TreeNodeFilterRadioList;        
        private Table _FormTable;

        #endregion

        #region Public Properties

        /// <summary>
        /// Used to pick a single person via the PeopleEditor SharePoint control.
        /// </summary>
        public PeopleEditor PeoplePicker
        {
            get
            {
                return _PeoplePicker;
            }
        }

        /// <summary>
        /// Lists the available SharePoint permission levels of the current Web. 
        /// </summary>
        public DropDownList RoleNameList
        {
            get
            {
                EnsureChildControls();
                return _RoleNameList;
            }
        }

        /// <summary>
        /// Place holder button control: Wireup to allow the end-user to generate the Tree Hierarchy.
        /// </summary>
        public Button GenerateTree
        {
            get
            {
                EnsureChildControls();
                return _GenerateTree;
            }
        }

        
        /// <summary>
        /// Radio button control allowing the end-user to select how they want to filter the permission tree.
        /// </summary>
        public RadioButtonList TreeNodeFilterRadioList
        {
            get
            {
                EnsureChildControls();
                return _TreeNodeFilterRadioList;
            }
        }

        /// <summary>
        /// The mode of the this Access Checker input form. Determines which input controls need to be rendered.
        /// </summary>
        public AccessCheckerMode Mode
        {
            get
            {
                return _Mode;
            }
            set
            {
                _Mode = value;
            }
        }

        /// <summary>
        /// The main table used in this input form
        /// </summary>
        protected Table EditorTable
        {
            get;
            set;
        }

        /// <summary>
        /// Get an SPPrincipal object of the user entered in the PeoplePicker control.
        /// </summary>
        public SPPrincipalInfo User
        {
            get
            {
                EnsureChildControls();
                if (_User == null)
                {
                    if (_PeoplePicker.ResolvedEntities.Count == 1)
                    {
                        try
                        {
                            PickerEntity pickerEntity = (PickerEntity)_PeoplePicker.ResolvedEntities[0];
                            _User = GetPrincipalInfo(pickerEntity.Key);
                        }
                        catch (Exception e)
                        {
                            throw new ApplicationException(Properties.Resources.User_Could_Not_Be_Resolved);
                        }
                    }                    
                }
                return _User;
            }
        }

        /// <summary>
        /// Get an SPRoleDefinition object of the selected Role Definition.
        /// </summary>
        public SPRoleDefinition AccessLevel
        {
            get
            {
                EnsureChildControls();
                if (_AccessLevel == null)
                {
                    int roleID = System.Convert.ToInt32(_RoleNameList.SelectedValue);

                    if (roleID > 0 && roleID <= Int32.MaxValue)
                        this._AccessLevel = this._ContextWeb.RoleDefinitions.GetById(roleID);
                    else
                        this._AccessLevel = null;
                }
                return _AccessLevel;
            }
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// Create the input controls for both modes of an AccessCheckerWebPart.
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Create a Toolbar control and add it to the main table
            _Toolbar = (ToolBar)_Page.LoadControl("/_controltemplates/ToolBar.ascx");

            // Add the Download Report hyperlink and render permissions tree button.
            HyperLink downloadReportLink = new HyperLink();
            downloadReportLink.Text = Properties.Resources.Download_Report;
            downloadReportLink.PreRender += new EventHandler(downloadReportLink_PreRender);
            _Toolbar.Buttons.Controls.Add(downloadReportLink);

            _Toolbar.Attributes["width"] = "100%";
            _Toolbar.Attributes["cellpadding"] = "2";

            _GenerateTree.Text = Properties.Resources.Go;
            _GenerateTree.PreRender += new EventHandler(_GenerateTree_PreRender);
            _Toolbar.RightButtons.Controls.Add(_GenerateTree);

            TableRow tr = new TableRow();
            TableCell td = new TableCell();
            td.Controls.Add(_Toolbar);
            tr.Cells.Add(td);
            AddFormRow(tr);

            _FormTable = new Table();
            _FormTable.Attributes["class"] = "ms-formtable ACWP_formtable";

            _PeoplePicker = new PeopleEditor();
            _RoleNameList = new DropDownList();

            _RoleNameList.ToolTip = Properties.Resources.Role_Name_List_Tool_Tip;

            _PeoplePicker.AutoPostBack = false;
            _PeoplePicker.ToolTip = Properties.Resources.People_Picker_Tool_Tip;
            _PeoplePicker.PlaceButtonsUnderEntityEditor = true;
            _PeoplePicker.ID = "PeopleEditor";
            _PeoplePicker.AllowEmpty = false;
            _PeoplePicker.SelectionSet = "User,SecGroup";
            _PeoplePicker.MultiSelect = false;

            foreach (SPRoleDefinition role in GetRoleDefinitionsOfContextWeb())
            {
                ListItem item = new ListItem(role.Name, role.Id.ToString());
                _RoleNameList.Items.Add(item);
            }

            _FormTable.Controls.Add(CreateFormRow(Properties.Resources.User_Login_Name_Label, new Control[] { _PeoplePicker }));
            _FormTable.Controls.Add(CreateFormRow(Properties.Resources.User_Role_Name_Label, new Control[] { _RoleNameList }));
            _TreeNodeFilterRadioList = CreateTreeNodeFilterRadioList();            
            _FormTable.Controls.Add(CreateFormRow(Properties.Resources.Filter_Options_Title, new Control[] { _TreeNodeFilterRadioList }));
            td.Controls.Add(_FormTable);

            this.Controls.Add(EditorTable);
        }

        
        /// <summary>
        /// Set the properties of the GenerateTree button that are dependant on the Mode variable.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _GenerateTree_PreRender(object sender, EventArgs e)
        {
            if (this._Mode == AccessCheckerMode.Web)
                _GenerateTree.Text = Properties.Resources.Apply_Filter_Button;            
        }

        /// <summary>
        /// Hide/Show input controls dependant on the Mode variable.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (this._Mode == AccessCheckerMode.Web)
            {
                if (_FormTable.Controls.Count > 2)
                {
                    _FormTable.Controls[0].Visible = false;
                    _FormTable.Controls[1].Visible = false;
                }
            }
            else
            {
                _GenerateTree.Text = Properties.Resources.Go;

                if (_FormTable.Controls.Count > 2)
                {
                    _FormTable.Controls[0].Visible = true;
                    _FormTable.Controls[1].Visible = true;
                }
            }
        }

        /// <summary>
        /// Set the downloadReportLink attributes that are dependant on the Mode variable.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downloadReportLink_PreRender(object sender, EventArgs e)
        {
            HyperLink linkButton = (HyperLink)sender;            
            PickerEntity pickerEntity = null;
            String loginAccountName = "";

            if (_PeoplePicker.ResolvedEntities.Count == 1)
            {
                pickerEntity = (PickerEntity)_PeoplePicker.ResolvedEntities[0];
                if (pickerEntity != null)
                    loginAccountName = pickerEntity.Key;
            }

            // Open the Report in a new window, which is used to download the xml report file
            linkButton.Target = "_blank";

            linkButton.NavigateUrl = this._ContextWeb.Url + Common.ACCESS_CHECKER_LAYOUTS_PATH +
                             "?Mode=" + HttpUtility.UrlEncode(this._Mode.ToString()) +
                             "&Login=" + HttpUtility.UrlEncode(loginAccountName) +
                             "&Access=" + HttpUtility.UrlEncode(_RoleNameList.SelectedItem.Text) +
                             "&DownloadReport=true";
        }

        /// <summary>
        /// Add a new Row to the primary Table within this input form.
        /// </summary>
        /// <param name="row"></param>
        protected void AddFormRow(TableRow row)
        {
            if (this.EditorTable == null)
            {
                 CreateFormTable();
            }
            this.EditorTable.Rows.Add(row);
        }

        /// <summary>
        /// Create the primary Table that all controls should be added to.
        /// </summary>
        protected void CreateFormTable()
        {
            this.EditorTable = new Table();
            this.EditorTable.CellPadding = 0;
            this.EditorTable.CellSpacing = 0;
            this.EditorTable.Style["border-collapse"] = "collapse";
            this.EditorTable.Attributes.Add("width", "100%");
        }

        /// <summary>
        /// Create a new TableRow control used for capturing input. 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="controls"></param>
        /// <returns></returns>
        protected TableRow CreateFormRow(string title, Control[] controls)
        {
            TableRow row = new TableRow();
            TableCell columnName = new TableCell();
            columnName.Attributes["class"] = "ms-formlabel";
            columnName.Controls.Add(new LiteralControl(title));
            row.Cells.Add(columnName);

            TableCell columnControl = new TableCell();
            columnControl.Attributes["class"] = "ms-formbody";
            foreach (Control control in controls)
            {
                columnControl.Controls.Add(control);
            }
            row.Cells.Add(columnControl);

            return row;

        }

        /// <summary>
        /// Return a SPPrincipalInfo object based on a string that contains the user name (DOMAIN\User_Alias).
        /// </summary>
        /// <param name="loginName">Name of the login.</param>
        private SPPrincipalInfo GetPrincipalInfo(string loginName)
        {
            SPPrincipalInfo principal = null;

            // Try to resolve the login based on the authentication mode retreived from SharePoint first. 
            // If that fails try other supported authentication methods.
            if (_AuthenticationMode == SupportedAuthenticationModes.Forms)
            {
                if ((principal = ResolveFormsPrincipal(loginName)) != null)
                    _AuthenticationMode = SupportedAuthenticationModes.Forms;
                else
                {
                    if ((principal = ResolveWindowsPrincipal(loginName)) != null)
                        _AuthenticationMode = SupportedAuthenticationModes.Windows;
                }
            }
            else if (_AuthenticationMode == SupportedAuthenticationModes.Windows)
            {
                if ((principal = ResolveWindowsPrincipal(loginName)) != null)
                    _AuthenticationMode = SupportedAuthenticationModes.Windows;
                else
                {
                    if ((principal = ResolveFormsPrincipal(loginName)) != null)
                        _AuthenticationMode = SupportedAuthenticationModes.Forms;
                }
            }
            else
                throw new ApplicationException(Properties.Resources.Authentication_Mode_Not_Supported);

            if (principal == null)
                throw new ArgumentException(Properties.Resources.Login_Name_Does_Not_Exist);

            return principal;
        }

        /// <summary>
        /// Resolves the forms principal.
        /// </summary>
        /// <param name="loginName">Name of the login.</param>
        /// <returns></returns>
        private SPPrincipalInfo ResolveFormsPrincipal(string loginName)
        {
            return SPUtility.ResolvePrincipal(this._ContextSite.WebApplication, SPUrlZone.Default, loginName, SPPrincipalType.User | SPPrincipalType.SecurityGroup, SPPrincipalSource.MembershipProvider | SPPrincipalSource.RoleProvider, false);
        }

        /// <summary>
        /// Resolves the windows principal.
        /// </summary>
        /// <param name="loginName">Name of the login.</param>
        /// <returns></returns>
        private SPPrincipalInfo ResolveWindowsPrincipal(string loginName)
        {
            return SPUtility.ResolveWindowsPrincipal(this._ContextSite.WebApplication, loginName, SPPrincipalType.User | SPPrincipalType.SecurityGroup, false);
        }

        /// <summary>
        /// Gets the role definitions of context web.
        /// </summary>
        /// <returns></returns>
        private SPRoleDefinitionCollection GetRoleDefinitionsOfContextWeb()
        {
            return this._ContextWeb.RoleDefinitions;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Create a new instance of a AccessCheckerInputForm object. To be used with an AccessCheckerWebPart instance.
        /// </summary>
        /// <param name="authMode"></param>
        /// <param name="page"></param>
        /// <param name="contextWeb"></param>
        /// <param name="contextSite"></param>
        public AccessCheckerInputForm(SupportedAuthenticationModes authMode, Page page, SPWeb contextWeb, SPSite contextSite)
            : base()
        {
            _Page = page;
            _AuthenticationMode = authMode;
            _ContextWeb = contextWeb;
            _ContextSite = contextSite;
            _GenerateTree = new Button();
        }


        /// <summary>
        /// Configure a ButtonRadioList control to display filter options for a SPPermissionsTreeView control.
        /// </summary>
        internal static void ConfigureTreeNodeFilterRadioList(RadioButtonList TreeNodeFilterRadioList, string TreeNodeFilter, AccessCheckerMode mode)
        {
            ListItem tempItem;
            bool selectedItemSet = false;

            if (TreeNodeFilterRadioList.SelectedItem != null)
                return;

            tempItem = TreeNodeFilterRadioList.Items.FindByValue(TreeNodeFilterState.HideMatchingPermissionNodes.ToString());
            if (TreeNodeFilter == TreeNodeFilterState.HideMatchingPermissionNodes.ToString())
            {
                tempItem.Selected = true;
                selectedItemSet = true;
            }
            // Clear any previously set value. This function is called twice when a user modifies the TreeNodeFilter
            // value via the Web Part Pane method. The stack trace is:
            // ConfigureTreeNodeFilterRadioList->ApplyChanges->ConfigureTreeNodeFilterRadioList
            else
                tempItem.Selected = false;
            if (tempItem != null && mode == AccessCheckerMode.User)
                tempItem.Text = Properties.Resources.User_Filter_Matching_Permission_Nodes;
            else
                tempItem.Text = Properties.Resources.Web_Filter_Matching_Permission_Nodes;
            tempItem = TreeNodeFilterRadioList.Items.FindByValue(TreeNodeFilterState.HideDifferentPermissionNodes.ToString());
            if (TreeNodeFilter == TreeNodeFilterState.HideDifferentPermissionNodes.ToString())
            {
                tempItem.Selected = true;
                selectedItemSet = true;
            }
            else
                tempItem.Selected = false;
            if (tempItem != null && mode == AccessCheckerMode.User)
                tempItem.Text = Properties.Resources.User_Filter_Different_Permission_Nodes;
            else
                tempItem.Text = Properties.Resources.Web_Filter_Different_Permission_Nodes;

            if (selectedItemSet != true)
            {
                tempItem = TreeNodeFilterRadioList.Items.FindByValue(TreeNodeFilterState.ShowAll.ToString());
                tempItem.Selected = true;
            }
        }

        /// <summary>
        /// Build a RadioButtonList control used to capture the filter option of a SPPermissionsTreeView control.
        /// </summary>
        internal static RadioButtonList CreateTreeNodeFilterRadioList()
        {
            RadioButtonList TreeNodeFilterRadioList = new RadioButtonList();
            TreeNodeFilterRadioList.Items.Add(new ListItem(Properties.Resources.Show_All_Nodes, TreeNodeFilterState.ShowAll.ToString()));
            TreeNodeFilterRadioList.Items.Add(new ListItem("", TreeNodeFilterState.HideMatchingPermissionNodes.ToString()));
            TreeNodeFilterRadioList.Items.Add(new ListItem("", TreeNodeFilterState.HideDifferentPermissionNodes.ToString()));
            TreeNodeFilterRadioList.RepeatDirection = RepeatDirection.Vertical;

            return TreeNodeFilterRadioList;
        }
        #endregion
    }    
}
